package handler

import (
	"context"
	"go-likaladi-shop/common/base_global"
	"go-likaladi-shop/common/base_util"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"google.golang.org/protobuf/types/known/emptypb"
	"gorm.io/gorm"
	"lw-shop/goods_service/model"
	"lw-shop/goods_service/proto"
)

func (s *GoodsService) GoodsList(ctx context.Context, req *proto.GoodsFilterRequest) (*proto.GoodsListResponse, error) {
	//关键词搜索、查询新品、查询热⻔商品、通过价格区间筛选， 通过商品分类筛选
	var goods []model.Goods
	db := base_global.DB.Model(model.Goods{})
	goodsCondition(db, req)

	var total int64
	if err := db.Count(&total).Error; err != nil {
		return nil, base_util.GrpcError(err)
	}

	if err := db.Scopes(base_util.Paginate(int(req.Pages), int(req.PagePerNums))).Find(&goods).Error; err != nil {
		return nil, base_util.GrpcError(err)
	}

	goodsListResponse, err := getGoodsListResponse2(goods)
	if err != nil {
		return nil, err
	}
	goodsListResponse.Total = int32(total)
	return goodsListResponse, nil
}

func (s *GoodsService) BatchGetGoods(ctx context.Context, req *proto.BatchGoodsIdInfo) (*proto.GoodsListResponse, error) {
	var goods []model.Goods
	//调⽤where并不会真正执⾏sql 只是⽤来⽣成sql的 当调⽤find， first才会去执⾏sql，
	if err := base_util.QueryByIds2(req.Id, &goods); err != nil {
		return nil, err
	}

	result, err := getGoodsListResponse(goods)
	if err != nil {
		return nil, err
	}
	result.Total = int32(len(goods))
	return result, nil
}

func (s *GoodsService) CreateGoods(ctx context.Context, req *proto.CreateGoodsInfo) (*proto.GoodsInfoResponse, error) {
	//这⾥没有看到图⽚⽂件是如何上传， 在微服务中 普通的⽂件上传已经不再使⽤
	var goods model.Goods
	if err := updateOrSave(&goods, req); err != nil {
		return nil, err
	}
	return &proto.GoodsInfoResponse{
		Id: goods.ID,
	}, nil
}

func (s *GoodsService) DeleteGoods(ctx context.Context, req *proto.DeleteGoodsInfo) (*emptypb.Empty, error) {
	if result := base_global.DB.Delete(&model.Goods{}, req.Id); result.RowsAffected == 0 {
		return nil, status.Errorf(codes.NotFound, "商品不存在")
	}
	return &emptypb.Empty{}, nil
}

func (s *GoodsService) UpdateGoods(ctx context.Context, req *proto.CreateGoodsInfo) (*emptypb.Empty, error) {
	var goods model.Goods
	if result := base_global.DB.First(&goods, req.Id); result.RowsAffected == 0 {
		return nil, status.Errorf(codes.InvalidArgument, "商品不存在")
	}
	if err := updateOrSave(&goods, req); err != nil {
		return nil, err
	}
	return &emptypb.Empty{}, nil
}

func (s *GoodsService) GetGoodsDetail(ctx context.Context, req *proto.GoodInfoRequest) (*proto.GoodsInfoResponse, error) {
	var goods model.Goods
	if result := base_global.DB.First(&goods, req.Id); result.RowsAffected == 0 {
		return nil, status.Errorf(codes.NotFound, "商品不存在")
	}

	return convertGoods(goods)
}

func getGoodsListResponse(goods []model.Goods) (*proto.GoodsListResponse, error) {
	var categoryIds []int32
	var brandIds []int32
	for _, obj := range goods {
		categoryIds = append(categoryIds, obj.CategoryID)
		brandIds = append(brandIds, obj.BrandsID)
	}

	brandDbMap, err := BrandDBMap(brandIds)
	if err != nil {
		return nil, err
	}

	categoriesMap, err := CategoryDBMap(categoryIds)
	if err != nil {
		return nil, err
	}

	goodsListResponse := &proto.GoodsListResponse{}
	var resp []*proto.GoodsInfoResponse

	for _, obj := range goods {
		data, err := formatGoods2(obj, brandDbMap, categoriesMap)
		if err != nil {
			return nil, err
		}
		resp = append(resp, data)
	}

	goodsListResponse.Data = resp

	return goodsListResponse, nil
}

func getGoodsListResponse2(goods []model.Goods) (*proto.GoodsListResponse, error) {
	categoryIds := base_util.SliceExtractDistinct(goods, func(t model.Goods) int32 {
		return t.CategoryID
	})
	brandIds := base_util.SliceExtractDistinct(goods, func(t model.Goods) int32 {
		return t.BrandsID
	})

	brandDbMap, err := BrandDBMap(brandIds)
	if err != nil {
		return nil, err
	}

	categoriesMap, err := CategoryDBMap(categoryIds)
	if err != nil {
		return nil, err
	}

	goodsListResponse := &proto.GoodsListResponse{}
	var resp []*proto.GoodsInfoResponse

	for _, obj := range goods {
		data, err := formatGoods2(obj, brandDbMap, categoriesMap)
		if err != nil {
			return nil, err
		}
		resp = append(resp, data)
	}

	goodsListResponse.Data = resp

	return goodsListResponse, nil
}

func goodsCondition(db *gorm.DB, req *proto.GoodsFilterRequest) {
	if req.KeyWords != "" {
		//搜索
		db.Where("name LIKE ?", "%"+req.KeyWords+"%")
	}
	if req.IsNew {
		db.Where(model.Goods{IsNew: true})
	}
	if req.PriceMin > 0 {
		db.Where("shop_price>=?", req.PriceMin)
	}
	if req.PriceMax > 0 {
		db.Where("shop_price<=?", req.PriceMax)
	}
	if req.Brand > 0 {
		db.Where("brand_id=?", req.Brand)
	}
}

func formatGoods(obj model.Goods,
	brandDbMap map[int32]*proto.BrandInfoResponse,
	categoriesMap map[int32]*proto.CategoryInfoResponse) (*proto.GoodsInfoResponse, error) {

	data, err := convertGoods(obj)
	if err != nil {
		return nil, err
	}

	if err := parseGoods(data, obj, brandDbMap, categoriesMap); err != nil {
		return nil, err
	}

	return data, nil
}

func formatGoods2(obj model.Goods,
	brandDbMap map[int32]*proto.BrandInfoResponse,
	categoriesMap map[int32]*proto.CategoryInfoResponse) (*proto.GoodsInfoResponse, error) {

	data := &proto.GoodsInfoResponse{} // 分配内存
	if err := base_util.Copy(&obj, data); err != nil {
		return nil, err
	}

	if err := parseGoods(data, obj, brandDbMap, categoriesMap); err != nil {
		return nil, err
	}

	return data, nil
}

func parseGoods(data *proto.GoodsInfoResponse, obj model.Goods,
	brandDbMap map[int32]*proto.BrandInfoResponse,
	categoriesMap map[int32]*proto.CategoryInfoResponse) error {

	sliceImg, err := base_util.FormatSlice(obj.Images)
	if err != nil {
		return err
	}
	sliceDescImages, err := base_util.FormatSlice(obj.DescImages)
	if err != nil {
		return err
	}
	data.Images = sliceImg
	data.DescImages = sliceDescImages

	if category := categoriesMap[obj.CategoryID]; category != nil {
		data.Category = &proto.CategoryBriefInfoResponse{
			Id:   category.Id,
			Name: category.Name,
		}
	}

	if brand := brandDbMap[obj.BrandsID]; brand != nil {
		data.Brand = &proto.BrandInfoResponse{
			Id:   brand.Id,
			Name: brand.Name,
			Logo: brand.Logo,
		}
	}
	return nil
}

func convertGoods(obj model.Goods) (*proto.GoodsInfoResponse, error) {
	result := &proto.GoodsInfoResponse{
		Id:              obj.ID,
		CategoryId:      obj.CategoryID,
		Name:            obj.Name,
		GoodsSn:         obj.GoodsSn,
		ClickNum:        int32(obj.ClickNum),
		SoldNum:         int32(obj.SoldNum),
		FavNum:          int32(obj.FavNum),
		MarketPrice:     obj.MarketPrice.String(),
		ShopPrice:       obj.ShopPrice.String(),
		GoodsBrief:      obj.GoodsBrief,
		ShipFree:        obj.ShipFree,
		GoodsFrontImage: obj.GoodsFrontImage,
		IsNew:           obj.IsNew,
		IsHot:           obj.IsHot,
		OnSale:          obj.OnSale,
		CreateTime:      uint64(obj.CreatedTime.Unix()),
		CreateTimeStr:   base_util.FormatDate(obj.CreatedTime),
	}
	result.Brand = &proto.BrandInfoResponse{Id: obj.BrandsID}
	result.CategoryId = obj.CategoryID

	if obj.Images != "" {
		var imgSlice []string
		if err := base_util.FromJSON(obj.Images, &imgSlice); err != nil {
			return nil, err
		}
		result.Images = imgSlice
	}

	if obj.DescImages != "" {
		var descImgSlice []string
		if err := base_util.FromJSON(obj.DescImages, &descImgSlice); err != nil {
			return nil, err
		}
		result.DescImages = descImgSlice
	}

	return result, nil
}

func updateOrSave(goods *model.Goods, req *proto.CreateGoodsInfo) error {
	if result := base_global.DB.First(&model.Category{}, req.CategoryId); result.RowsAffected == 0 {
		return base_util.LogicError("商品分类不存在")
	}
	if result := base_global.DB.First(&model.Brands{}, req.BrandId); result.RowsAffected == 0 {
		return base_util.LogicError("品牌不存在")
	}

	if err := base_util.Copy(req, &goods); err != nil {
		return err
	}

	marketPrice, err := base_util.FormatDecimal(req.MarketPrice)
	if err != nil {
		return err
	}
	shopPrice, err := base_util.FormatDecimal(req.ShopPrice)
	if err != nil {
		return err
	}

	goods.ShopPrice = shopPrice
	goods.MarketPrice = marketPrice

	if req.Images != nil && len(req.Images) > 0 {
		imgStr, err := base_util.ToJSON(req.Images)
		if err != nil {
			return err
		}
		goods.Images = imgStr
	}
	if req.DescImages != nil && len(req.DescImages) > 0 {
		descImageStr, err := base_util.ToJSON(req.DescImages)
		if err != nil {
			return err
		}
		goods.DescImages = descImageStr
	}
	goods.IsDeleted = false
	base_global.DB.Save(&goods)
	return nil
}
